﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Collections;
using CommonTypes;
using System.Threading;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;

public delegate void MyDelegate(string s);

namespace PuppetMaster
{
    public partial class Form1 : Form
    {
        //Lista de users e servidores
        public Dictionary<String, ClientInfo> usersList = new Dictionary<string, ClientInfo>();
        public Dictionary<String, ServerInfo> serversList = new Dictionary<string, ServerInfo>();
        //delegate para poder requisitar a alteração de um component na form por parte de uma thread
        public MyDelegate del;
        public int waitTime = 0;

        public Form1()
        {
            InitializeComponent();
            TcpChannel channel = new TcpChannel();
            ChannelServices.RegisterChannel(channel, true);
            del = new MyDelegate(UpdateMsgs);
        }
        //Botao de browse
        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.ShowDialog();
            
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Invoke(this.del, new Object[] { "[PuppetMaster initiated]\r\n" });
        }


        private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
        {
            Thread t = new Thread(new ThreadStart(createClients));
            t.Start();
            //createClients();
            
        }
        //faz parse dos comandos carregados num ficheiro
        public void createClients()
        {
            Console.WriteLine(openFileDialog1.FileName);

            StreamReader sr = new StreamReader(openFileDialog1.FileName);
            String line;
            String[] words;
            String username;

            while ((line = sr.ReadLine()) != null)
            {
                
                if (line.StartsWith("connect"))
                {
                    words = line.Split(new Char[] { ' ' });
                    Console.WriteLine(words[0] + ": ");

                    username = words[1];
                    String ipPort = words[2];

                    connectTask(username, ipPort);
                    continue;
                    
                }
                if (line.StartsWith("disconnect"))
                {
                    words = line.Split(new Char[] { ' ' });

                    Console.WriteLine(words[0]+ ": ");

                    username = words[1];
                    disconnectTask(username);
                    continue;
                }
                if (line.StartsWith("reservation"))
                {
                    reservationTask(line);
                    continue;
                }
                if (line.StartsWith("readCalendar"))
                {
                    username = line.Split(new Char[]{' '})[1];
                    readCalendarTask(username);
                    continue;
                }
                if (line.StartsWith("wait")) {
                    words = line.Split(new Char[]{' '});
                    waitTime = Int32.Parse(words[1]);
                    Thread.Sleep(waitTime * 1000);
                    Console.WriteLine("wait");
                }
                if (line.StartsWith("shutdown")) {
                    shutDownTask();
                }

            }
        }
        //manda um cliente connectar-se
        private void connectTask(String username, String ipPort) {

            ClientInfo clientInfo;
            IClient client;

            if(username.StartsWith("central")){

                initServer(username,ipPort);
                Console.WriteLine(username + " registou-se com sucesso e está disponível no porto " + ipPort);
                this.Invoke(this.del, new Object[] {"[connect] - " + username + " " + ipPort});
                return;
            }

            try {
                clientInfo = usersList[username];
                clientInfo.ClientRef.connect(username, ipPort);
                usersList[username].IpPort = ipPort;
                clientInfo.IsOnline = true;
            }
            catch (KeyNotFoundException e)
            {
                //Process creation
                Process newProcess = new Process();
                newProcess.StartInfo.FileName = "ClientApp1.exe";
                newProcess.StartInfo.Arguments = username + " " + ipPort + " " + serversList["central-1"].IpPort;
                newProcess.Start();

                //Obtain client reference
                clientInfo = new ClientInfo(null, username, ipPort, newProcess);
                usersList.Add(username, clientInfo);
            }

            client = (IClient)Activator.GetObject(typeof(IClient), "tcp://" + clientInfo.IpPort + "/client");
            usersList[username].ClientRef = client;
            Console.WriteLine(username + " registou-se com sucesso e está disponível no porto " + ipPort);
            this.Invoke(this.del, new Object[] { "[connect] - " + username + " " + ipPort});
        }
        //manda um cliente disconnectar-se
        private void disconnectTask(String username) {

            ClientInfo clientInfo;
            IClient client;

            
            if(username.StartsWith("central")){
                shutDownServer(username);
                Console.WriteLine("O servidor " + username + " desconectou-se com sucesso!");
                this.Invoke(this.del, new Object[] {"[disconnect] - " + username});
                return;
            }

            clientInfo = usersList[username];
            clientInfo.ClientRef.disconnect(username);
            usersList[username].IsOnline = false;
            Console.WriteLine("O cliente " + username + " desconectou-se com sucesso!");
            this.Invoke(this.del, new Object[] {"[disconnect] - " + username});
        }
        //manda um cliente dar inicio a uma reserva
        private void reservationTask(String reservationCommand)
        {
            String[] splitedCommand = reservationCommand.Split(new char[] { '{', '}', ';' });

            String description = splitedCommand[1];
            String[] users = splitedCommand[2].Split(new Char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
            String[] slots = splitedCommand[3].Split(new Char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
            List<int> slotsList = new List<int>();

            foreach (String user in users.ToList<String>())
            {

                Console.WriteLine(user);
            }
            foreach (String slot in slots)
            {
                Console.WriteLine(slot);
                slotsList.Add(Int32.Parse(slot));
            }
            String host = users[0];

            IClient client = usersList[host].ClientRef;

            client.hostReservation(description, users.ToList<String>(), slotsList);
            this.Invoke(this.del, new Object[] { "[reservation] - " + host});
        }
        //pede para ler o calendario de um cliente
        private void readCalendarTask(String username) {

            ClientInfo clientInfo = usersList[username];

            this.Invoke(this.del, new Object[] { "[readcalendar] - " + username });
            Console.WriteLine("Calendar - " + clientInfo.Username);
            this.Invoke(this.del, new Object[] { "Calendar - " + clientInfo.Username});
            SlotsToPuppet[] slots = clientInfo.ClientRef.readCalendar();
           
            //resultsTextBox.Text += "Calendar - "+clientInfo.Username+"\r\n";
            foreach (SlotsToPuppet slot in slots) {
                //resultsTextBox.Text += "Slot: " + slot.Number + " Estado: " + slot.State + "\r\n";
                Console.WriteLine("Slot: " + slot.Number + " Estado: " + slot.State);
                this.Invoke(this.del, new Object[] { "Reservation: "+ slot.ReservationID +" Slot: " + slot.Number + " Estado: " + slot.State});
            }
            this.Invoke(this.del, new Object[] {"***************"});
            Console.WriteLine("***************");
        }
        //Termina todos os processos
        private void shutDownTask(){
            foreach(ClientInfo client in usersList.Values){
                if(!client.ClientProcess.HasExited)
                    client.ClientProcess.Kill();
            }
            foreach (ServerInfo server in serversList.Values)
            {
                if(!server.ServerProcess.HasExited)
                    server.ServerProcess.Kill();
            }
            Thread.CurrentThread.Abort();
        }
        //inicia uma replica
        private void initServer(String serverName,String serverIpPort) {
            
            IServer server;

            Process serverProcess = new Process();
            serverProcess.StartInfo.FileName = "ServerApp.exe";
            serverProcess.StartInfo.Arguments = serverName + " " + serverIpPort;
            serverProcess.Start();


            server = (IServer)Activator.GetObject(typeof(IServer), "tcp://" + serverIpPort + "/Server");
            ServerInfo serverinfo = new ServerInfo(serverName, server, serverIpPort, serverProcess);
            try
            {
                serversList[serverName] = serverinfo;
            }catch(KeyNotFoundException){
                serversList.Add(serverName, serverinfo);
            }
        }
        //termina uma replica
        private void shutDownServer(String serverName) {
            serversList[serverName].ServerProcess.Kill();
        }
        //le cada comando dado na textbox dos comandos
        private void submitButton_Click(object sender, EventArgs e)
        {
            String command = comandTextBox.Text;
            String[] parametros = command.Split(new Char[]{' '});

            
            if (parametros[0].Equals("connect")) {
                connectTask(parametros[1], parametros[2]);
            }
            if (parametros[0].Equals("disconnect")) {
                disconnectTask(parametros[1]);
            }
            if (parametros[0].Equals("readCalendar"))
            {
                readCalendarTask(parametros[1]);
            }
            if (parametros[0].Equals("reservation"))
            {
                reservationTask(command);
            }
            if (parametros[0].Equals("wait")) {
                waitTime = Int32.Parse(parametros[1]);
                Thread.Sleep(waitTime);
            }
            if (parametros[0].Equals("shutdown")) {
                shutDownTask();
            }
            comandTextBox.Text = "";
        }
        //escreve na textbox que mostra o resultado dos comandos
        public void UpdateMsgs(String newMsg)
        {
            this.resultsTextBox.Text += newMsg + "\r\n";
        }
    }
}
